home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Frameworks / Hsoi's App Shell 1.0a4 / Hsoi's App Shell Source / HASWindows.c < prev   
Encoding:
C/C++ Source or Header  |  1997-01-28  |  67.1 KB  |  2,343 lines  |  [TEXT/CWIE]

  1. /*
  2.     HASWindows.c from Hsoi's App Shell © 1995-1997 John C. Daub.  All rights reserved.
  3.  
  4.     This file deals with window related stuff:  from the creation and destruction of windows,
  5.     to window events (drags, content clicks, etc), to the little utility routines that
  6.     help it all.
  7. */
  8.  
  9. #pragma mark ••• #includes •••
  10.  
  11. #ifndef _WASTE_
  12. #include "WASTE.h"
  13. #endif
  14. #include "HASGlobals.h"
  15. #ifndef __HSOIS_APP_SHELL__
  16. #include "HASMain.h"
  17. #endif
  18. #include "HASDialogs.h"
  19. #include "HASMenus.h"
  20. #include "HASUtilCursors.h"
  21. #include "HASMenuWindows.h"
  22. #include "HASFiles.h"
  23. #include "HASUtilFiles.h"
  24. #include "HASWindows.h"
  25. #include "HASLongControls.h"
  26. #include "HASUtilities.h"
  27. #include "HASUtilPStrings.h"
  28. #include "HASPrinting.h"
  29. #include "HASSoundSpeech.h"
  30.  
  31. #include "WETabs.h"
  32.  
  33. #ifndef _WASTEOBJECTS_
  34. #include "WASTE_Objects.h"
  35. #endif
  36.  
  37. #if HAS_DEBUG
  38. #include "HASUtilTest.h"
  39. #endif
  40.  
  41. #pragma mark -
  42. #pragma mark ••• Globals •••
  43.  
  44. //    local global variables
  45.  
  46. //static    WETranslateDragUPP    sWEDragTranslator = nil;
  47. static    WEScrollUPP            sWEScroller = nil;
  48. static    ControlActionUPP    sScrollProc = nil;
  49. static    long                sScrollStep = 0;        // how many pixels to scroll (used by ScrollProc)
  50.  
  51.  
  52. #pragma mark -
  53. #pragma mark ••• Window Utils •••
  54.  
  55. // this calculates the rect for the grow icon (the little thing in the lower right
  56. // hand corner of the window)
  57.  
  58. void    HsoiCalcGrowIconRect( WindowRef window, Rect *iconRect )
  59. {
  60.     Rect portRect = GetWindowPort(window)->portRect;
  61.     
  62.     iconRect->top = portRect.bottom - (kScrollBarWidth - 2);
  63.     iconRect->left = portRect.right - (kScrollBarWidth - 2);
  64.     iconRect->bottom = portRect.bottom;
  65.     iconRect->right = portRect.right;
  66.     
  67.     return;
  68. }
  69.  
  70. // this calcs the rect within the window where we draw the text...basically,
  71. // from the window's portRect, inset things a bit for scrollbars and then a
  72. // bit more so text doesn't get slammed up against the edges
  73.  
  74. void    HsoiCalcTextRect( WindowRef window, Rect *textRect )
  75. {
  76.     Rect portRect = GetWindowPort(window)->portRect;
  77.     
  78.     textRect->top = 0;
  79.     textRect->left = 0;
  80.     textRect->bottom = portRect.bottom - (kScrollBarWidth - 1);
  81.     textRect->right = portRect.right - (kScrollBarWidth - 1);
  82.     InsetRect( textRect, kTextMargin, kTextMargin );
  83.     
  84.     return;
  85. }
  86.  
  87. // calculate the rect that would enclose the scrollbars
  88.  
  89. void    HsoiCalcScrollBarRect( WindowRef window, VHSelect axis, Rect *barRect )
  90. {
  91.     Rect    portRect = GetWindowPort(window)->portRect;
  92.     
  93.     switch (axis)
  94.     {
  95.         case v:
  96.             barRect->top = -1;
  97.             barRect->left = portRect.right - (kScrollBarWidth - 1);
  98.             barRect->bottom = portRect.bottom - (kScrollBarWidth - 2);
  99.             barRect->right = portRect.right + 1;
  100.         break;
  101.         
  102.         case h:
  103.             barRect->top = portRect.bottom - (kScrollBarWidth - 1);
  104.             barRect->left = -1;
  105.             barRect->bottom = portRect.bottom + 1;
  106.             barRect->right = portRect.right - (kScrollBarWidth - 2);
  107.         break;
  108.         
  109.         default:
  110.         break;
  111.     }
  112.     
  113.     return;
  114. }
  115.  
  116.  
  117. /*
  118.     HsoiCalcIdealWindowSize() attempts to determine the "ideal" size of a document window
  119.     (the standard state of the window).  This "ideal" window would be one in which the
  120.     area of the window in which text is drawn (basically, the dest/view Rects of the
  121.     WE instance) is the same as the area on which the text would be printed (taking
  122.     into account the size of the printer's paper, the margin settings from the prefs).
  123.     Then, pixels will be added for scroll bars etc...  This is in an attempt to give
  124.     a WYSIWYG look to the document windows...i always disliked how some text editors
  125.     would give you some certain (or random) window size cause when you went to print,
  126.     everything would be resized...no way really to ensure that what you see on the
  127.     screen and what you print can be the same (such as where line breaks would occur
  128.     and stuff).
  129.     
  130.     But like i said, this function only calculates the ideal size.  The Rect that
  131.     you pass as an argument doesn't contain coordinates of where the ideal window
  132.     should be, but rather just contains measurements (for example, subtracting
  133.     rect.left from rect.right would give you the ideal width of the window, again
  134.     taking not only the text rect into account, but also the right/horizontal
  135.     scroll bar and some buffer pixels).  these measurements would give you the
  136.     size of the window's portRect/contRgn.rgnBBox (see next comment block).
  137.     If you want to get the window's strucRgn.rgnBBox, you'll have to manipulate
  138.     this idealRect some (see HsoiZoomWindow for an instance where i do this)
  139.     
  140. */
  141.  
  142. /*
  143.     This is also a good time to talk a little bit about the various regions of a window.
  144.     There are many regions to a window (size region, drag region, close region, and zoom
  145.     region will won't be discussing here, nor the update region, etc).  What i want to
  146.     talk about are the content region and the structure region.
  147.     
  148.     First, there is what's known as the window frame.  the frame is comprised of the
  149.     title bar (including the close and zoom boxes, if present), and the outline of
  150.     the window itself.  The drawing of the window frame is handled by the Window Manager
  151.     (the WM also draws the size box, but only when YOU call the DrawGrowIcon function).
  152.     Then there is the content region.  This region you are responsible for drawing.
  153.     The content region is just that: the contents of the window.  This can include
  154.     the size box, window controls (including any scroll bars you might have), and
  155.     of course the window contents (like text or a PICT or whatever).  When you put
  156.     the window frame and the content region together, this makes up the structure
  157.     region (the entire screen area occupied by a window, including the window outline,
  158.     title bar, and content region).
  159.     
  160.     These various regions (content and structure) are parts of the WindowRecord (from
  161.     which WindowPeek's are derrived.  I dunno how this will all change with Copland.
  162.     I'm sure the window parts will remain, but how the data structure of a WindowRef
  163.     is constructed we'll just have to wait until the official Copland API is released
  164.     to see).  The strucRgn and contRgn members of the WindowRecord are expressed
  165.     in global coordinates.  It is important to know this because of the following:
  166.     Part of the WindowRecord is a GrafPtr.  This GrafPtr contains the window's
  167.     portRect.  the portRect (becomes important with calls like SizeWindow) of the
  168.     window is equal to the content region of the window, however the portRect is
  169.     expressed in local coordinates (while the contRgn is expressed in global).
  170.     
  171.     For more information on this stuff, see Inside Macintosh: Toolbox Essentials.
  172.     Chapter 4 is all about the Window Manager, and specifically pages 4-12, 4-13
  173.     cover this information about window regions.
  174. */
  175.  
  176.  
  177. void    HsoiCalcIdealWindowSize( Rect *idealRect )
  178. {
  179.     LongRect        workLongRect;
  180.     Rect            margins;
  181.         
  182.     // first, let's get the rough area for the ideal size
  183.     
  184.     // we need to determine the margins based upon the preferences
  185.     // we pass a "kludgy" number for the direction to force HsoiInchesToDots
  186.     // to return a value * 72 dpi (see HsoiInchesToDots and IM:Imaging With QuickDraw
  187.     // 5-32 (the note under ScreenRes()) for more information as to why i did this)
  188.     
  189.     margins.left = HsoiInchesToDots( gMyPrefs.printLeftMargin, kForce72dpi );
  190.     margins.right = HsoiInchesToDots( gMyPrefs.printRightMargin, kForce72dpi );
  191.     margins.top = HsoiInchesToDots( gMyPrefs.printTopMargin, kForce72dpi );
  192.     margins.bottom = HsoiInchesToDots( gMyPrefs.printBottomMargin, kForce72dpi );
  193.  
  194.     // now get the dimensions of the actual printed rect
  195.     
  196.     HsoiGetPrintRect( &margins, &workLongRect, false );
  197.     WELongRectToRect( &workLongRect, idealRect );
  198.     
  199.     // now idealRect contains a rough estimate of what the window's text rect
  200.     // would be.  but we need to expand this rect to essentially be the size
  201.     // as the window's structure region.
  202.  
  203.     // first, add some pixels to the right and bottom for the scroll bars
  204.     // (these measurements and numbers must be in sync with HsoiCalcScrollBarRect()'s
  205.     // numbers so things don't get redrawn funky).
  206.     
  207.     // vertical/right scroll bar
  208.     
  209.     (*idealRect).right += kScrollBarWidth + 2;
  210.     
  211.     // horizontal/bottom scroll bar
  212.         
  213.     (*idealRect).bottom += kScrollBarWidth + 2;
  214.     
  215.     // inset (grow) the rect a bit (for happiness with HsoiCalcTextRect)
  216.     
  217.     InsetRect( idealRect, -kTextMargin, -kTextMargin );
  218.     
  219.     // and now idealRect contains our ideal size (measurements that can be used
  220.     // to figure out the portRect/contRgn.rgnBBox) so just return!
  221.         
  222.     return;
  223. }
  224.  
  225. // this takes a window and ensures that it's not extending off the edges of the
  226. // monitor(s).  if it is, bump it back onto the screen
  227.  
  228. void    HsoiAdjustWindowIntoScreen( WindowRef window )
  229. {
  230.     Rect        deskRect;
  231.     Rect        portRect = GetWindowPort( window )->portRect;
  232.     GrafPtr        savePort;
  233.     
  234.     // back in the day (the day?  when exactly was that?) :)
  235.     // anyways, before we really had to deal with multiple monitor setups (i.e. before
  236.     // the advent of Color QuickDraw), we could just get all the monitor real estate
  237.     // from screenBits.bounds.  but now with Color Quickdraw, all that's changed.
  238.     // so, if the computer has color quickdraw, we get ALL the real estate that might
  239.     // be there...but if they don't have color qd, we know they can't have a multi
  240.     // monitor setup, so just get things from qd.screenBits.bounds
  241.     
  242.     if ( gHasColorQD )
  243.         deskRect = (*GetGrayRgn())->rgnBBox;
  244.     else
  245.         deskRect = qd.screenBits.bounds;
  246.     
  247.     // set our port to the window to be adjusted (this is necessary so that
  248.     // LocalToGlobal and GlobalToLocal know how to convert coordinates)
  249.     
  250.     GetPort( &savePort );
  251.     SetPortWindowPort( window );
  252.     
  253.     // convert our window's portRect to global coordinates for comparison
  254.     // against the deskRect
  255.     
  256.     LocalToGlobal( &topLeft(portRect) );
  257.     LocalToGlobal( &botRight(portRect) );
  258.     
  259.     // bump the window's portRect within the screen area
  260.     
  261.     // first, the right side
  262.     
  263.  
  264.     while ( portRect.right + 5 > deskRect.right )
  265.         portRect.right--;
  266.             
  267.     // and now the bottom side
  268.     
  269.     while ( portRect.bottom + 5 > deskRect.bottom )
  270.         portRect.bottom--;
  271.         
  272.     // (i probably ought to do the top and left also, and frankly to really make this
  273.     // robust, i ought to ensure this window is totally on 1 monitor (not spread across
  274.     // 2 or more monitors) and if not, move it totally within one monitor's screen.
  275.     // if this troubles you, feel free to adjust this function!  you might want to
  276.     // look at HsoiZoomWindow for an idea of how to deal with multiple monitors)
  277.     
  278.     // convert the portRect back to local coordinates
  279.     
  280.     GlobalToLocal( &topLeft(portRect) );
  281.     GlobalToLocal( &botRight(portRect) );
  282.     
  283.     // and assign this new portRect back to the window record. (hopefully this
  284.     // won't break under Copland....we'll have to wait and see)
  285.     
  286.     GetWindowPort(window)->portRect = portRect;
  287.  
  288.     return;
  289. }
  290.  
  291. #pragma mark -
  292. #pragma mark ••• Window/ScrollBar Utils •••
  293.  
  294. // does just what you think...draws the grow icon in the lower right corner
  295. // of the window
  296.  
  297. void    HsoiMyDrawGrowIcon( WindowRef window, Boolean validate )
  298. {
  299.     GrafPtr        savePort;
  300.     RgnHandle    saveClip;
  301.     Rect        r;
  302.     
  303.     //    save port and set the port to wind
  304.     
  305.     GetPort( &savePort );
  306.     SetPortWindowPort( window );
  307.     
  308.     //    save the clip region
  309.     saveClip = NewRgn();
  310.     GetClip( saveClip );
  311.     
  312.     //    calculate grow icon rect
  313.     HsoiCalcGrowIconRect( window, &r );
  314.     
  315.     //    set clip region to grow icon rect
  316.     ClipRect( &r );
  317.     
  318.     //    call _DrawGrowIcon
  319.     
  320.     DrawGrowIcon( window );
  321.     
  322.     //    if validate is true, remove the grow icon rect from the update region
  323.     if ( validate )
  324.         ValidRect( &r );
  325.     
  326.     //    restore old clip region
  327.     
  328.     SetClip( saveClip );
  329.     DisposeRgn( saveClip );
  330.     
  331.     //    restore old port
  332.     SetPort( savePort );
  333.         
  334.     return;
  335. }
  336.  
  337. // if the value of the scroll bar has changed (cause perhaps someone clicked in it),
  338. // call this to adjust things
  339.  
  340. void    HsoiScrollBarChanged( WindowRef window )
  341. {
  342.     WEReference    we;
  343.     LongRect    viewRect, destRect;
  344.  
  345.     // get the window's WE instance
  346.         
  347.     we = HsoiGetWindowWE( window );
  348.     
  349.     // get the view and dest rects
  350.     
  351.     WEGetViewRect( &viewRect, we );
  352.     WEGetDestRect( &destRect, we );
  353.     
  354.     // and scroll the text
  355.     
  356.     WEScroll( viewRect.left - destRect.left - HsoiLCGetValue( ((*HsoiGetWindowDocument(window))->scrollBars).h), 
  357.         viewRect.top - destRect.top - HsoiLCGetValue( ((*HsoiGetWindowDocument(window))->scrollBars).v), we );
  358.     
  359.     return;
  360. }
  361.  
  362. // this adjusts and updates our scroll bars to keep them in sync with the text
  363.  
  364. void    HsoiAdjustBars( WindowRef window )
  365. {
  366.     DocumentHandle    hDocument;
  367.     WEReference        we;
  368.     GrafPtr            savePort;
  369.     LongRect        viewRect, destRect;
  370.     long            value;
  371.     long            max;
  372.     ControlRef        bar;
  373.     
  374.     GetPort( &savePort );
  375.     SetPortWindowPort( window );
  376.     
  377.     // get the view and destination rectangle
  378.     
  379.     hDocument = HsoiGetWindowDocument(window);
  380.     we = (*hDocument)->we;
  381.  
  382.     WEGetViewRect( &viewRect, we );
  383.     WEGetDestRect( &destRect, we );
  384.         
  385.     //    do the vertical axis
  386.     
  387.     //    get scroll bar handle
  388.     
  389.     bar = ((*hDocument)->scrollBars).v;
  390.     
  391.     //    calculate new scroll bar settings
  392.     
  393.     //    NOTE:  (destRect.bottom - destRect.top) always equals the total text height because
  394.     //    WASTE automatically updates destRect.bottom whenever line breaks are recalculated
  395.     
  396.     value = viewRect.top - destRect.top;
  397.  
  398.     // bug fix from Dan Crevier (thanx Dan). I was adding viewRect + viewRect...dumb... 
  399.     
  400.     max = value + (destRect.bottom - viewRect.bottom);
  401.     
  402.     //    make sure max is always non-negative
  403.     
  404.     if ( max <= 0 )
  405.         max = 0;
  406.         
  407.     //    reset the scroll bar
  408.     
  409.     HsoiLCSetMax( bar, max );
  410.     HsoiLCSetValue( bar, value );
  411.     
  412.     //    if value exceeds max then the bottom of the destRect is above
  413.     //    the bottom of the view rectangle:  we need to scroll the text downward
  414.     
  415.     if ( value > max )
  416.         HsoiScrollBarChanged( window );
  417.     
  418.     //    now do the horizontal axis
  419.     
  420.         //    get scroll bar handle
  421.     
  422.     bar = ((*hDocument)->scrollBars).h;
  423.     
  424.     //    calculate new scroll bar settings
  425.     
  426.     //    NOTE:  (destRect.bottom - destRect.top) always equals the total text height because
  427.     //    WASTE automatically updates destRect.bottom whenever line breaks are recalculated
  428.     
  429.     value = viewRect.left - destRect.left;
  430.     
  431.     //    again, bug fix from Dan Crevier (thanx again). I was adding viewRect.right and viewRect.right
  432.     //    when it should be:
  433.     
  434.     
  435.     max = value + (destRect.right - viewRect.right);
  436.     
  437.     //    make sure max is always non-negative
  438.     
  439.     if ( max <= 0 )
  440.         max = 0;
  441.         
  442.     //    reset the scroll bar
  443.     
  444.     HsoiLCSetMax( bar, max );
  445.     HsoiLCSetValue( bar, value );
  446.     
  447.     //    if value exceeds max then the bottom of the destRect is above
  448.     //    the bottom of the view rectangle:  we need to scroll the text downward
  449.     
  450.     if ( value > max )
  451.         HsoiScrollBarChanged( window );
  452.     
  453.     SetPort( savePort );
  454.     
  455.     return;
  456. }
  457.  
  458.  
  459. //    Fix it's scroll bars and WE view rect when the window is created
  460. //    or after it is resized or zoomed, or when the page is adjusted
  461.  
  462. void    HsoiViewChanged( WindowRef window )
  463. {
  464.     DocumentHandle    hDocument;
  465.     GrafPtr            savePort;
  466.     ControlRef        bar;
  467.     Rect            r;
  468.     LongRect        viewRect;
  469.     
  470.     GetPort( &savePort );
  471.     SetPortWindowPort( window );
  472.     
  473.     hDocument = HsoiGetWindowDocument(window);
  474.     
  475.     //    recalculate the correct rectangles for the text area and the scroll bars,
  476.     //    based on the window's port rect
  477.     
  478.     HsoiCalcTextRect( window, &r );
  479.     WERectToLongRect( &r, &viewRect );
  480.     
  481.     //    resize the text area
  482.     
  483.     WESetViewRect( &viewRect, (*hDocument)->we );
  484.     
  485.     //     more and resize the control bars
  486.     
  487.     //    first, the vertical bars
  488.     
  489.     bar = ((*hDocument)->scrollBars).v;
  490.     HsoiCalcScrollBarRect( window, v, &r );
  491.     MoveControl( bar, r.left, r.top );
  492.     SizeControl( bar, r.right - r.left, r.bottom - r.top );
  493.     ValidRect( &r );
  494.     
  495.     //    now the horizontal bars
  496.     
  497.     bar = ((*hDocument)->scrollBars).h;
  498.     HsoiCalcScrollBarRect(window, h, &r);
  499.     MoveControl( bar, r.left, r.top );
  500.     SizeControl( bar, r.right - r.left, r.bottom - r.top );
  501.     ValidRect( &r );
  502.     
  503.     //    reset the thumb positions and the max values of the control bars
  504.     HsoiAdjustBars( window );
  505.     
  506.     //    redraw the control bars
  507.     
  508.  
  509.     ShowControl( ((*hDocument)->scrollBars).v );
  510.     ShowControl( ((*hDocument)->scrollBars).h );
  511.     
  512.     SetPort( savePort );
  513.     
  514.     return;
  515.  
  516. }    
  517.  
  518. #pragma mark -
  519. #pragma mark ••• Window Event Handlers •••
  520.  
  521. // this resizes our window, like when the window is to be grown via the grow box
  522. // see HsoiDoGrow() for more info...
  523.  
  524. void    HsoiResize( Point newSize, WindowRef window )
  525. {
  526.     DocumentHandle    hDocument;
  527.     GrafPtr            savePort;
  528.     Rect            r;
  529.     RgnHandle        tempRgn, dirtyRgn;
  530.     
  531.     GetPort( &savePort );
  532.     SetPortWindowPort( window );
  533.     
  534.     hDocument = HsoiGetWindowDocument(window);
  535.     
  536.     //    create temporarty regions for calculations
  537.     tempRgn = NewRgn();
  538.     dirtyRgn = NewRgn();
  539.     
  540.     //    save old text region
  541.     
  542.     HsoiCalcTextRect( window, &r );
  543.     RectRgn( tempRgn, &r );
  544.         
  545.     //    erase the old grow icon rect
  546.     HsoiCalcGrowIconRect( window, &r );
  547.     EraseRect( &r );
  548.     
  549.     //    hide the scroll bars
  550.     
  551.     HideControl( ((*hDocument)->scrollBars).v );
  552.     HideControl( ((*hDocument)->scrollBars).h );
  553.     
  554.     //    perform the actual resizing of the window, redraw scroll bars and grow icon
  555.     SizeWindow( window, newSize.h, newSize.v, false );
  556.     HsoiViewChanged( window );
  557.     HsoiMyDrawGrowIcon( window, true );
  558.     
  559.     //    calculate the dirty region (to be updated)
  560.     HsoiCalcTextRect( window, &r );
  561.     RectRgn( dirtyRgn, &r );
  562.     XorRgn( dirtyRgn, tempRgn, dirtyRgn );
  563.     InsetRect( &r, -kTextMargin, -kTextMargin );
  564.     RectRgn( tempRgn, &r );
  565.     SectRgn( dirtyRgn, tempRgn, dirtyRgn );
  566.     
  567.     //    mark the dirty region as invalid
  568.     
  569.     InvalRgn( dirtyRgn );
  570.     
  571.     //    throw away temporary regions
  572.     
  573.     DisposeRgn( tempRgn );
  574.     DisposeRgn( dirtyRgn );
  575.     
  576.     SetPort( savePort );
  577.     
  578.     return;
  579. }
  580.  
  581. // this is the function called when we're growing the window.
  582.  
  583. void    HsoiDoGrow( Point hitPt, WindowRef window )
  584. {
  585.     Rect        sizeRect;
  586.     long        newSize;
  587.     Point        tempPoint;
  588.     
  589.     SetRect( &sizeRect, kMinWindowWidth, kMinWindowHeight, MAXSHORT, MAXSHORT );
  590.     newSize = GrowWindow( window, hitPt, &sizeRect );
  591.     
  592.         
  593.     //    In the WASTE Demo App source, Marco typecasted newSize to a type Point.  Can't
  594.     //    do that in C (but you can, obviously, in Pascal).    
  595.     //    But there is a trick!  The Point structure is 32-bits, with the v value
  596.     //    in the hi word and the h value in the low word.  So, we can just separate
  597.     //    them out.  Neat huh?
  598.         
  599.     
  600.             
  601.     if ( newSize != 0 )
  602.     {
  603.         tempPoint.v = HiWrd( newSize );
  604.         tempPoint.h = LoWrd( newSize );
  605.         
  606.         HsoiResize( tempPoint, window );
  607.     }
  608.     return;
  609. }
  610.  
  611. /*
  612.     HsoiZoomWindow() is a replacement function for the toolbox call, ZoomWindow().
  613.     ZoomWindow is ok, but isn't always the smartest way to do things, especially
  614.     in a multiple monitor setup (for example, the user might have moved the window
  615.     to a different screen since it was last zoomed).
  616.     
  617.     This "replacement" procedure (for ZoomWindow) is pretty much taken stock
  618.     from Inside Macintosh:Imaging with QuickDraw:Graphics Devices:5-10
  619.     except that I've translated it all into C and am working towards as much
  620.     Copland compatability as possible.
  621.     
  622.     Here's what Apple has to say about it:
  623.     
  624.     This is a procedure "which an application might call when the user clicks the
  625.     zoom box.  Because the user might have moved the window to a different screen
  626.     since it was last zoomed, the procedure first determines which screen contains
  627.     the largest area of the window and then calcuates the ideal window size for
  628.     that screen before zooming the window.
  629.     
  630.     The screen calculations in the [HsoiZoomWindow] procedure compare GDevice
  631.     records stored in the device list (if Color QuickDraw is not available,
  632.     [HsoiZoomWindow] assumes that it's running on a computer with a single screen."
  633.     
  634.     After the sample code, Apple adds:
  635.     
  636.     "If the user is zooming the window to the standard state, [HsoiZoomWindow] calculates
  637.     a new standard size and location based on the application's own considerations,
  638.     the current location of the window, and the available screens.  The [HsoiZoomWindow]
  639.     procedure always places the standard state on the screen where the window is currently
  640.     displayed or, if the window spans screens, on the scren containing the largest
  641.     area of the window.
  642.     
  643.     [This sample code listing] uses the QuickDraw routines GetDeviceList, TestDeviceAttribute,
  644.     GetNextDevice, SectRect, and GetMainDevice to examine characteristics of
  645.     the available screens as stored in GDevice records.  Most of the code in
  646.     [this sample code listing] is devoted to determining which screen should display
  647.     the window in the standard state.
  648.     
  649.     **IMPORTANT**
  650.     Never use the bounds field of a PixMap record to determine the size of the
  651.     screen; instead, use the value of the gdRect field of the GDevice record for
  652.     the screen (as shown here)
  653.     
  654.     After calculating the standard state, if necessary, [HsoiZoomWindow] calls
  655.     the ZoomWindow procedure to redraw the window frame in the new size and
  656.     location."
  657.     
  658.     In a future release of Hsoi's App Shell, I hope to modify this a little bit.
  659.     I would like to have it such that the window's "standard state" (as opposed
  660.     to the "user state") is the size (the text editing part...not counting things
  661.     like the title bar and scroll bars) of the window is the size of the paper
  662.     (to keep with as much WYSIWYG as possible).  That is, if the user will
  663.     be printing on an 8.5" x 11" piece of paper, the window is 8.5 x 11.  If
  664.     an envelope, the window is the size of an envelope.  Of course, all things
  665.     will take into account the screen size (so that windows don't extend offscreen
  666.     somewhere).  See IM:Imaging with QuickDraw: 5-9 for more details.
  667.     
  668.     update:  i've done the above...based upon the size of the paper to be
  669.     printed on (and other things, like margins), the standard state will now
  670.     be "WYSIWYG" and happy for the user.
  671.  
  672. */
  673.  
  674. void    HsoiZoomWindow( WindowRef thisWindow, short zoomInOrOut )
  675. {
  676.     GDHandle            gdNthDevice, gdZoomOnThisDevice;
  677.     GrafPtr                savePort;
  678.     Rect                windRect, zoomRect, theSect, portRect;
  679.     long                sectArea, greatestArea;
  680.     short                wTitleHeight;
  681.     Boolean                sectFlag;
  682.     RgnHandle            windStructureRgn = NewRgn();
  683.     
  684.     // first, get the old port and set the new port to our window
  685.     
  686.     GetPort( &savePort );
  687.     SetPortWindowPort( thisWindow );
  688.     
  689.     // erase the window to avoid flicker
  690.     
  691.     EraseRect( &GetWindowPort( thisWindow )->portRect );
  692.     
  693.     // if zooming to standard state
  694.     
  695.     if ( zoomInOrOut == inZoomOut )
  696.     {
  697.         // assume a single screen (you need color quickdraw in order to support
  698.         // multiple monitors...no color qd, no way they'd have more than 1 monitor)
  699.         // and...
  700.         
  701.         if ( !gHasColorQD )
  702.         {
  703.             // ... set standard state to the full screen
  704.             
  705.             // zoomRect = qd.screenBits.bounds;
  706.             // InsetRect( &zoomRect, 4, 4 );
  707.             
  708.             // we're not gonna do the above...we want to have the window zoomed
  709.             // to an "ideal" size.  see below for more information
  710.             
  711.             HsoiCalcIdealWindowSize( &portRect );
  712.             
  713.             // now set our zoomRect to the proper global coordinates.
  714.             
  715.             SetRect( &zoomRect, qd.screenBits.bounds.left + 3,
  716.                     qd.screenBits.bounds.top + kTitleBarHeight + LMGetMBarHeight() + 3,
  717.                     portRect.right - portRect.left + 3,
  718.                     portRect.bottom - portRect.top + 3 );
  719.  
  720.             if ( zoomRect.right > qd.screenBits.bounds.right )
  721.                 zoomRect.right = qd.screenBits.bounds.right - 2;
  722.             
  723.             if ( zoomRect.bottom > qd.screenBits.bounds.bottom )
  724.                 zoomRect.bottom = qd.screenBits.bounds.bottom - 7;            
  725.  
  726.             SetWindowStandardState( thisWindow, &zoomRect );
  727.         }
  728.         else // locate the window on available screens
  729.         {
  730.             windRect = GetWindowPort( thisWindow )->portRect;
  731.             
  732.             // convert to global coordinates
  733.             
  734.             LocalToGlobal( &topLeft(windRect) );
  735.             LocalToGlobal( &botRight(windRect) );
  736.             
  737.             // calculate the height of the window's title bar
  738.             
  739.             GetWindowStructureRgn( thisWindow, windStructureRgn );
  740.             wTitleHeight = windRect.top - 1 - (*windStructureRgn)->rgnBBox.top;
  741.             windRect.top -= wTitleHeight;
  742.             
  743.             // get the first screen
  744.  
  745.             gdNthDevice = GetDeviceList();
  746.             greatestArea = 0; // initialize area to zero
  747.             
  748.             // check window against all gdRects in gDevice list and remember
  749.             // which gdRect contains largest area of window
  750.             
  751.             while ( gdNthDevice != nil )
  752.             {
  753.                 if ( TestDeviceAttribute( gdNthDevice, screenDevice ) &&
  754.                         TestDeviceAttribute( gdNthDevice, screenActive ) )
  755.                 {
  756.                     // the SectRect function calculates the intersection of the window
  757.                     // rectangle and this GDevice's boundary rectangle and returns
  758.                     // true if the rectangles intersect, false if they don't
  759.                     
  760.                     sectFlag = SectRect( &windRect, &(*gdNthDevice)->gdRect, &theSect );
  761.                     
  762.                     // determine which screen holds greatest window area
  763.                     // first, calculate area of rectangle on current screen
  764.                     
  765.                     sectArea = ((long)theSect.right - (long)theSect.left) *
  766.                                 ((long)theSect.bottom - (long)theSect.top);
  767.                     
  768.                     if ( sectArea > greatestArea )
  769.                     {
  770.                         // set greatest area so far
  771.                         
  772.                         greatestArea = sectArea;
  773.                         
  774.                         // set zoom device
  775.                         
  776.                         gdZoomOnThisDevice = gdNthDevice;
  777.                     }
  778.                     
  779.                     // get next GDevice record
  780.                     
  781.                     gdNthDevice = GetNextDevice( gdNthDevice );
  782.                 }
  783.             
  784.             } // end: while ( gdNthDevice != nil )
  785.             
  786.             
  787.             // if gdZoomOnThisDevice is on main device, allow for menu bar height
  788.             
  789.             if ( gdZoomOnThisDevice == GetMainDevice() )
  790.                 wTitleHeight += LMGetMBarHeight();
  791.             
  792.             // create the zoom rectangle
  793.             
  794.             // now, the commented stuff that follows is what was in the original
  795.             // code example from Inside Macintosh.  it sets the zoom rectangle to the
  796.             // full screen, minus window title height (and menu bar height if necessary),
  797.             // inset by 3 pixels.
  798.  
  799.             // incidentally, this is what ZoomWindow() normally does.  So why have this
  800.             // routine to "replace" ZoomWindow()?  cause remember, ZoomWindow() doesn't
  801.             // know how to deal with mulitple monitor setups (hopefully, Apple will
  802.             // change this for Copland)
  803.             
  804. //            SetRect( &zoomRect, (*gdZoomOnThisDevice)->gdRect.left + 3,
  805. //                                (*gdZoomOnThisDevice)->gdRect.top + wTitleHeight + 3,
  806. //                                (*gdZoomOnThisDevice)->gdRect.right - 3,
  807. //                                (*gdZoomOnThisDevice)->gdRect.bottom - 3 );
  808.  
  809.             // but since we're cool and have our own ideal/standard window state, let's
  810.             // figure out the ideal size of the window (remember, this will
  811.             // return measurements (not coordinates) that would give you
  812.             // the portRect/contRgn.rgnBBox of the window.  we'll need to adjust to make
  813.             // this rect the size of the structure region
  814.             
  815.             HsoiCalcIdealWindowSize( &portRect );
  816.             
  817.             // now set our zoomRect to the proper global coordinates.
  818.             
  819.             SetRect( &zoomRect, (*gdZoomOnThisDevice)->gdRect.left + 3,
  820.                     (*gdZoomOnThisDevice)->gdRect.top + wTitleHeight + 3,
  821.                     portRect.right - portRect.left + 3,
  822.                     portRect.bottom - portRect.top + 3 );
  823.             
  824.             // adjust in case the window is extending off the edges of the screen
  825.             // (we don't have to do top and left cause if you notice in SetRect,
  826.             // the top and left of the window are determined by the device the
  827.             // window is currently on).
  828.             
  829.             if ( zoomRect.right > (*gdZoomOnThisDevice)->gdRect.right )
  830.                 zoomRect.right = (*gdZoomOnThisDevice)->gdRect.right - 2;
  831.             
  832.             if ( zoomRect.bottom > (*gdZoomOnThisDevice)->gdRect.bottom )
  833.                 zoomRect.bottom = (*gdZoomOnThisDevice)->gdRect.bottom - 7;            
  834.             
  835.             // set up the WStateData record for this window.
  836.             
  837.             SetWindowStandardState( thisWindow, &zoomRect );
  838.                             
  839.         }
  840.     
  841.     } // end if ( zoomInOrOut == inZoomOut )
  842.     //  if zoomInOrOut == inZoomIn, just let ZoomWindow zoom to user state
  843.     
  844.     
  845.     // zoom the window frame
  846.     
  847.     ZoomWindow( thisWindow, zoomInOrOut, (thisWindow == FrontWindow()) );
  848.     
  849.     // in the Inside Macintosh sample that HsoiZoomWindow() was based upon, here
  850.     // they called MyResizeWindow(thisWindow), which is an application-defined
  851.     // window sizing routine.  However, due to how HsoiDoZoom() is set up, we
  852.     // don't need to do that stuff here (it's done in HsoiDoZoom() instead)
  853.     
  854.     // dispose of the region
  855.     
  856.     if ( windStructureRgn != nil )
  857.         DisposeRgn( windStructureRgn );
  858.     
  859.     // restore the port
  860.     
  861.     SetPort( savePort );
  862.     
  863.     return;
  864. }
  865.  
  866. // this is the function called when a zoom is called for
  867.  
  868. void    HsoiDoZoom( short partCode, WindowRef window )
  869. {
  870.     DocumentHandle    hDocument;
  871.     GrafPtr            savePort;
  872.     Rect            r;
  873.     
  874.     // set up the ports
  875.     
  876.     GetPort( &savePort );
  877.     SetPortWindowPort( window );
  878.     
  879.     // get a handle to the DocumentRecord
  880.     
  881.     hDocument = HsoiGetWindowDocument(window);
  882.     
  883.     // erase our port rect (for better redrawing)
  884.     
  885.     r = GetWindowPort( window )->portRect;
  886.     EraseRect( &r );
  887.     
  888.     // hide the controls (else risk a messy looking zoom)
  889.     
  890.     HideControl( ((*hDocument)->scrollBars).v );
  891.     HideControl( ((*hDocument)->scrollBars).h );
  892.     
  893.     // do the actual zooming
  894.     
  895.     HsoiZoomWindow( window, partCode );
  896.     
  897.     // and get things updated...
  898.     
  899.     // note the view has been changed and readjust
  900.     
  901.     HsoiViewChanged( window );
  902.     
  903.     // find the new text rect...
  904.     
  905.     HsoiCalcTextRect( window, &r );
  906.     
  907.     // ...and invalidate it for updating by the Window Manager
  908.     
  909.     InvalRect( &r );
  910.     
  911.     // restore the old port, and off we go!
  912.     
  913.     SetPort( savePort );
  914.     
  915.     return;
  916. }
  917.  
  918. // this is a callback routine called by the Toolbox Control Manager
  919. // move the scroll bar thumb and scroll the text accordingly
  920.  
  921.  
  922. pascal    void    hsoiScrollProc( ControlRef bar, ControlPartCode partCode )
  923. {
  924.     long        value, step;
  925.     
  926.     if ( partCode == kControlNoPart )    // if partCode == 0 (kControlNoPart), 
  927.                                         // then mouse is outside of the control..return...
  928.         return;
  929.     
  930.     // get the value of the scrollbar
  931.     
  932.     value = HsoiLCGetValue( bar );
  933.     
  934.     // find our step
  935.     
  936.     step = sScrollStep;
  937.  
  938.     // if the value of the scrollbar is less than max and greater and zero, and
  939.     // the value is greater than the min (zero) and less than the step...
  940.     
  941.     if ( (( value < HsoiLCGetMax( bar )) && ( step > 0 )) || (( value > 0 ) && ( step < 0 ) ) )
  942.     {
  943.         // ...set the value of the bar at += step...
  944.         
  945.         HsoiLCSetValue( bar, value + step );
  946.         
  947.         // ...and update the scroll bars (scroll the text)
  948.         
  949.         HsoiScrollBarChanged( FrontWindow() );
  950.     }
  951.  
  952.     return;
  953. }
  954.  
  955. // when the mouse is clicked in a scrollbar, we call this.
  956.  
  957. void    HsoiDoScrollBar( Point hitPt, EventModifiers modifiers, WindowRef window )
  958. {
  959.     DocumentHandle        hDocument;
  960.     ControlRef            bar;
  961.     LongRect            viewRect;
  962.     ControlPartCode        partCode;
  963.     short                step;
  964.     
  965.     hDocument = HsoiGetWindowDocument(window);
  966.     WEGetViewRect(&viewRect, (*hDocument)->we);
  967.     
  968.     //    find out which scrollbar was hit (if any) and in which part
  969.     partCode = FindControl( hitPt, window, &bar );
  970.     
  971.     if ( bar != nil )
  972.     {
  973.         //    dispatch on partCode
  974.     
  975.         if ( partCode == kControlIndicatorPart )
  976.         {
  977.             // click in thumb: call TrackControl with no actionProc and adjust text
  978.             
  979.             partCode = TrackControl( bar, hitPt, nil );
  980.             
  981.             // keep the MacOS short scrollbar values in sync with our long values
  982.             
  983.             HsoiLCSynch( bar );
  984.             
  985.             // and actually scroll the text (the WEScroll call)
  986.             
  987.             HsoiScrollBarChanged( window );
  988.             
  989.         } // end if partCode == inThumb
  990.     
  991.         else
  992.  
  993.         {
  994.             // the vertical scroll bar
  995.             
  996.             if ( bar == ((*hDocument)->scrollBars).v )
  997.             {
  998.             
  999.                 // dispatch our partCode
  1000.             
  1001.                 switch( partCode )
  1002.                 {
  1003.                     // when they click-hold in the up/down arrows, scroll by
  1004.                     // the value of kScrollDelta, but if the option key is held
  1005.                     // down while they scroll, only scroll by one pixel (allows
  1006.                     // for more "delicate" scrolling)
  1007.                 
  1008.                     case kControlUpButtonPart:
  1009.                         if ( (modifiers & optionKey ) == 0 )
  1010.                             step = -kScrollDelta;
  1011.                         else
  1012.                             step = -1;
  1013.                 
  1014.                     break;
  1015.                 
  1016.                     case kControlDownButtonPart:
  1017.                         if ( (modifiers & optionKey ) == 0 )
  1018.                             step = +kScrollDelta;
  1019.                         else
  1020.                             step = 1;
  1021.                 
  1022.                     break;
  1023.                 
  1024.                     // if they clicked in the page up/down parts, jump a "page"
  1025.                     
  1026.                     case kControlPageUpPart:
  1027.                         step = -( viewRect.bottom - viewRect.top) + kScrollDelta;
  1028.             
  1029.                     break;
  1030.                 
  1031.                     case kControlPageDownPart:
  1032.                         step = ( viewRect.bottom - viewRect.top ) - kScrollDelta;
  1033.             
  1034.                     break;
  1035.                 
  1036.                     default:
  1037.                         step = 0;
  1038.                 }
  1039.             }
  1040.             
  1041.             // else if the horizontal scroll bar...
  1042.             
  1043.             else if ( bar == ((*hDocument)->scrollBars).h )
  1044.             {            
  1045.                 // dispatch our partCode
  1046.             
  1047.                 switch( partCode )
  1048.                 {
  1049.                     case kControlUpButtonPart:
  1050.                         if ( (modifiers & optionKey ) == 0 )
  1051.                             step = -kScrollDelta;
  1052.                         else
  1053.                             step = -1;
  1054.                 
  1055.                     break;
  1056.                 
  1057.                     case kControlDownButtonPart:
  1058.                         if ( (modifiers & optionKey ) == 0 )
  1059.                             step = +kScrollDelta;
  1060.                         else
  1061.                             step = 1;
  1062.                 
  1063.                     break;
  1064.                 
  1065.                     case kControlPageUpPart:
  1066.                         step = -( viewRect.right - viewRect.left) + kScrollDelta;
  1067.             
  1068.                     break;
  1069.                 
  1070.                     case kControlPageDownPart:
  1071.                         step = ( viewRect.right - viewRect.left ) - kScrollDelta;
  1072.             
  1073.                     break;
  1074.                 
  1075.                     default:
  1076.                         step = 0;
  1077.                 }
  1078.             }
  1079.     
  1080.             //    save step in a static variable for our ScrollProc callback
  1081.         
  1082.             sScrollStep = step;
  1083.         
  1084.             //    track the mouse
  1085.         
  1086.             if ( sScrollProc == nil )
  1087.                 sScrollProc = NewControlActionProc( hsoiScrollProc );
  1088.  
  1089.             partCode = TrackControl( bar, hitPt, sScrollProc );
  1090.             
  1091.             if ( sScrollProc != nil )
  1092.             {
  1093.                 DisposeRoutineDescriptor( sScrollProc );
  1094.                 sScrollProc = nil;
  1095.             }
  1096.         }
  1097.  
  1098.     }
  1099.     
  1100.     return;
  1101. }
  1102.  
  1103. // this is a callback routine called whenever the text is scrolled automatically.
  1104. // Since auto-scrolling is enabled, WEScroll may be invoked internally by WASTE
  1105. // in many different circumstances, and we want to be notified when this happens
  1106. // so we can adjust the scroll bars
  1107.  
  1108. pascal void    hsoiTextScrolled( WEReference we )
  1109. {
  1110.     WindowRef    window = nil;
  1111.     
  1112.     //    retrieve the window pointer stored in the WE instance as a "reference constant"
  1113.     
  1114.     if (WEGetInfo(weRefCon, &window, we) != noErr )
  1115.         return;
  1116.     
  1117.     //    make sure the scroll bars are in synch with the destination rectangle
  1118.     
  1119.     HsoiAdjustBars( window );
  1120.  
  1121.     return;
  1122. }
  1123.  
  1124. //    handle the use of the pageup, pagedown, home and end keys on the extended keyboard
  1125.  
  1126. void    HsoiDoScrollKey( SignedByte keyCode, WindowRef window )
  1127. {
  1128.     DocumentHandle        hDocument;
  1129.     ControlRef            bar;
  1130.     long                v;
  1131.     LongRect            viewRect;
  1132.     
  1133.     hDocument = HsoiGetWindowDocument(window);
  1134.     bar = ((*hDocument)->scrollBars).v;
  1135.         
  1136.     //    get current scroll bar value
  1137.     
  1138.     v = HsoiLCGetValue( bar );
  1139.     
  1140.     //    get text view rect
  1141.     
  1142.     WEGetViewRect( &viewRect, (*hDocument)->we );
  1143.     
  1144.     switch ( keyCode )
  1145.     {
  1146.     
  1147.         case keyPgUp:
  1148.             v = v - ( viewRect.bottom - viewRect.top ) + kScrollDelta;
  1149.         
  1150.         break;
  1151.         
  1152.         case keyPgDn:
  1153.             v = v + ( viewRect.bottom - viewRect.top ) - kScrollDelta;
  1154.         
  1155.         break;
  1156.         
  1157.         case keyHome:
  1158.             v = 0;
  1159.         
  1160.         break;
  1161.         
  1162.         case keyEnd:
  1163.             v = MAXLONG;
  1164.         break;
  1165.         
  1166.         default:
  1167.         
  1168.         break;
  1169.     }    // end switch keyCode
  1170.     
  1171.     
  1172.     //    set the new scroll bar value and scroll the text pane accordingly
  1173.     
  1174.     HsoiLCSetValue( bar, v );
  1175.     HsoiScrollBarChanged( window );
  1176.     
  1177.     
  1178.     return;
  1179. }
  1180.  
  1181. // this is what's called when the user chooses Close from the File menu.
  1182.  
  1183. OSErr    HsoiDoClose( ClosingOption closing, SavingOption saving, WindowRef window )
  1184. {
  1185.     Str255            title, quitOrClose;
  1186.     short            alertResult;
  1187.     OSErr            err = noErr;
  1188.     
  1189.     // it should never hit this (cause if window == nil, the close menu item should be
  1190.     // disabled anyways, therefore no way to call this function.  but let's be complete
  1191.     
  1192.     if ( window == nil )
  1193.         return err;
  1194.         
  1195.     // first, check the window kind...if a DA or a dialog, close it properly
  1196.     
  1197.     if ( HsoiIsDAWindow( window ) )
  1198.     {
  1199.         CloseDeskAcc( GetWindowKind(window) );
  1200.         return err;
  1201.     }
  1202.     
  1203.     
  1204.     if ( HsoiIsDialogWindow( window ) )
  1205.     {
  1206.         HideWindow( window );
  1207.         return err;
  1208.     }
  1209.  
  1210.     // we'll just hide our clipboard window
  1211.     
  1212.     if ( HsoiIsClipboardWindow( window ) )
  1213.     {
  1214.         // if there was a sound in the clipboard window that was playing, we should
  1215.         // stop it from playing.  however, there isn't an easy way via Michael
  1216.         // Kamprath's WASTE Object Handlers to determine what window (WASTE instance)
  1217.         // the currently playing sound belongs to.  maybe I'll write one and
  1218.         // submit it to Michael.
  1219.         
  1220.         // so, to facilitate things, we could do 1 of 2 things.  instead of hiding
  1221.         // the clipboard window, destroy it.  i don't want to do that.  so,
  1222.         // if there is a sound playing, any sound, we'll just stop it.
  1223.         
  1224.         if ( SoundIsPlaying() )
  1225.             StopCurrentSound();
  1226.             
  1227.         HideWindow( window );
  1228.         
  1229.         return err;
  1230.     }
  1231.  
  1232. #if HAS_DEBUG
  1233.     
  1234.     if ( HsoiIsTestWindow( window ) )
  1235.     {
  1236.         HsoiDestroyTestWindow();
  1237.         return err;
  1238.     }
  1239. #endif
  1240.     
  1241.     // is the window dirty?
  1242.     
  1243.     if ( WEGetModCount( HsoiGetWindowWE(window) ) > 0 )
  1244.     {
  1245.         // do we have to ask the user whether to save changes??
  1246.         
  1247.         if ( saving == savingAsk )
  1248.         {
  1249.             // since we're asking, stop any currently playing sounds before the
  1250.             // dialog is brought up
  1251.             
  1252.             if ( SoundIsPlaying() )
  1253.                 StopCurrentSound();
  1254.         
  1255.             // get the title of the window/document
  1256.             
  1257.             GetWTitle( window, title );
  1258.             
  1259.             // just in case there is no title, stick in a dummy title
  1260.             
  1261.             if ( title == NIL_STRING )
  1262.                 HsoipStringCopy( "\puntitled", title );
  1263.             
  1264.             // get the string "closing" or "quitting" so that when this
  1265.             // alert is brought up, the text will br appropriate to the
  1266.             // context (try it!  it's a nice subtle touch...launch the app,
  1267.             // type something in the window, don't save, close the window,
  1268.             // it'l say "before closing", cancel, then quit, it'll say
  1269.             // "before quitting" and then just don't save and quit.  cute eh?)
  1270.             
  1271.             GetIndString( quitOrClose, rQuitCloseStrings, 1 + (closing ? 1 : 0 ) );
  1272.             
  1273.             // put the strings in the alert
  1274.             
  1275.             ParamText( title, quitOrClose, NIL_STRING, NIL_STRING );
  1276.             
  1277.             // put up the Save Changes? alert box?
  1278.             
  1279.             InitCursor();
  1280.                     
  1281.             alertResult = Alert( rSaveChangesAlert, HsoiGetMyStandardDialogFilter() );
  1282.             
  1283.             // exit if the user canceled the alert box
  1284.             
  1285.             if ( alertResult == kButtonCancel )
  1286.             {
  1287.                 err = userCanceledErr;
  1288.                 return err;
  1289.             }
  1290.             
  1291.             if ( alertResult == kButtonSave )
  1292.                 saving = savingYes;
  1293.             else
  1294.                 saving = savingNo;
  1295.         }
  1296.         
  1297.         if ( saving == savingYes )
  1298.         {
  1299.             // no need to check for and possibly stop a sound here...HsoiDoSave takes
  1300.             // care of that for us.
  1301.             
  1302.             // save it
  1303.             
  1304.             err = HsoiDoSave( window );
  1305.             if ( err != noErr )
  1306.             {
  1307.                 return err;
  1308.             }
  1309.         }
  1310.     } // end dirty window check
  1311.     
  1312.     // destroy the window
  1313.     
  1314.     HsoiDestroyWindow( window );
  1315.     
  1316.     return err;
  1317. }
  1318.  
  1319.  
  1320.  
  1321. //JCD - This "morally correct" code for window dragging is per an article in MacTech
  1322. //JCD - Magazine (July 1994, Vol 10, No. 7) by Eric Shapiro (of Rock Ridge Enterprises)
  1323. //JCD - called "Multiple Monitors vs. Your Application".
  1324. //JCD - 
  1325. //JCD - Eric addresses numerous things to allow your app to deal nicely with multiple
  1326. //JCD - monitor setups, one of them is Dragging.
  1327. //JCD - 
  1328. //JCD - According to Eric, many apps don't let you drag windows to second monitors, and
  1329. //JCD - though holding down the cmd/option keys often overrides this problem, it should
  1330. //JCD - still be updated.  And the only reason that qd.screenBits.bounds works to allow
  1331. //JCD - you to drag to second monitors is because of a kludge Apple put in the Window Manager
  1332. //JCD - 
  1333. //JCD - So what follows is Eric's "morally correct" code for dragging windows. thanx eric!
  1334.  
  1335. void    HsoiDoDrag( Point thePoint, WindowRef window )
  1336. {
  1337.     Rect        limitR;
  1338.  
  1339.     if ( gHasColorQD )
  1340.         limitR = ( *GetGrayRgn())->rgnBBox;
  1341.     else
  1342.         limitR = qd.screenBits.bounds;
  1343.                         
  1344.     DragWindow( window, thePoint, &limitR );
  1345.  
  1346.     return;
  1347. }
  1348.  
  1349.  
  1350. //    Handle clicks inside a window
  1351.  
  1352. Boolean    HsoiDoContentClick( Point hitPt, const EventRecord *event, WindowRef window )
  1353. {
  1354.     WEReference    we = HsoiGetWindowWE(window);
  1355.     long        selStart, selEnd;
  1356.     RgnHandle    selRgn;
  1357.     Boolean        inBackground, handleClick;
  1358.     Rect        textRect;
  1359.     GrafPtr        savePort;
  1360.     Boolean        result = false; // false means click should not activate this window
  1361.     
  1362.     
  1363.     //    is this window in the background?
  1364.     
  1365.     if ( IsWindowHilited(window) )
  1366.         inBackground = false;
  1367.     else
  1368.         inBackground = true;
  1369.  
  1370. #if HAS_DEBUG
  1371.     // if it's the test window, handling is pretty easy since it can't take
  1372.     // drags or really any other sort of click
  1373.  
  1374.     if ( HsoiIsTestWindow(window) )    
  1375.         return inBackground;
  1376. #endif
  1377.  
  1378.     // we don't want to let the below code handle anything but windows with
  1379.     // WASTE instances attached to them (else we risk crashing!).  so, before
  1380.     // we go further down, let's check for a few things
  1381.     
  1382.     if ( HsoiIsDialogWindow(window) || HsoiIsDAWindow( window ) )
  1383.         return inBackground;
  1384.  
  1385.     // one last test just in case...cause if we have a nil WE instance, we will
  1386.     // crash.
  1387.     
  1388.     if ( we == nil )
  1389.     {
  1390.         // if window is valid (i.e. !nil), we can return based upon the
  1391.         // inBackground setting to still allow a "valid" return
  1392.         
  1393.         if ( window != nil )
  1394.             return inBackground;
  1395.         else
  1396.         {
  1397.             // we got a BIG problem that needs to be tracked down....window should
  1398.             // never be nil due to where this function is called from (in HsoiDoEvent
  1399.             // from the mouseDown inContent...that event shouldn't even happen
  1400.             // if window == nil
  1401.             
  1402.             SysBeep( 5 );    // lame error handling
  1403.             return false;    // return false so hopefully nothing will happen upon return
  1404.         }
  1405.     }
  1406.  
  1407.     //    set the port to our window's port
  1408.     
  1409.     GetPort( &savePort );
  1410.     SetPortWindowPort( window );
  1411.     
  1412.     //    convert the point to local coordinates
  1413.     
  1414.     GlobalToLocal( &hitPt );
  1415.     
  1416.     //    a click in an inactive window should normally activate it,
  1417.     //    but the availability of the Drag Manager introduces an exception to this rule:
  1418.     //    a click in the background selection may start a drag gesture,
  1419.     //    without activating the window
  1420.     
  1421.     if ( inBackground )
  1422.     {
  1423.         if ( gHasDragAndDrop )
  1424.         {
  1425.             // see if the click was in a selection in a background window and
  1426.             // based on that, determine if we should handle the click or not
  1427.             
  1428.             WEGetSelection( &selStart, &selEnd, we );
  1429.             selRgn = WEGetHiliteRgn( selStart, selEnd, we );
  1430.             handleClick = PtInRgn( hitPt, selRgn ) && WaitMouseMoved( event->where );
  1431.             DisposeRgn( selRgn );
  1432.         }
  1433.         else
  1434.             handleClick = false; // no DragManager: never click-through
  1435.     }
  1436.     else
  1437.         handleClick = true;    // window is frontmost: always handle click
  1438.         
  1439.     if ( handleClick )
  1440.     {
  1441.         //    get view Rectangle in short coordinates
  1442.         
  1443.         HsoiCalcTextRect( window, &textRect );
  1444.         
  1445.         // if the click is in our text rect, let WEClick handle it (nice that
  1446.         // due to the fact WASTE has Internet Config support built into it,
  1447.         // this one little call can be Internet Config happy!)
  1448.                 
  1449.         if ( PtInRect( hitPt, &textRect ) )
  1450.         {
  1451.             if ( !gHiliting )
  1452.                 WEClick( hitPt, event->modifiers, event->when, we );
  1453.         }
  1454.         else
  1455.             // not in the text rect, scroll the bars
  1456.             
  1457.             HsoiDoScrollBar( hitPt, event->modifiers, window );
  1458.     }
  1459.     else
  1460.         result = inBackground;
  1461.     
  1462.     //    restore the port
  1463.     
  1464.     SetPort( savePort );
  1465.     
  1466.     
  1467.     return result;
  1468. }
  1469.  
  1470. #pragma mark -
  1471. #pragma mark ••• Creation/Destruction •••
  1472.  
  1473.  
  1474. /*    whenever an error kicks in ( err != noErr ), we should destroy partially allocated
  1475.     data structures to avoid memory leaks...make sure to add that stuff in!  When you
  1476.     do, stick it just before the return statement.  It's a good thing to call
  1477.     DisposePtr() after NewPtr() (or DisposeHandle() after NewHandle()) to make sure
  1478.     the heap don't get too fragmented up...see p. 33 of Knaster's How to Write for
  1479.     more info... */
  1480.     
  1481. OSErr    HsoiCreateWindow( const FSSpec *pFileSpec )
  1482. {
  1483.     DocumentHandle    hDocument;
  1484.     WindowRef        window;
  1485.     WEReference        we;
  1486.     ControlRef        bar;
  1487.     FInfo            fileInfo;
  1488.     Rect            textRect;
  1489.     LongRect        longTextRect;
  1490.     OSErr            err = noErr;
  1491.     AliasHandle        alHandle;
  1492.     Rect            windowRect;
  1493.     Str255            titleString;
  1494.     Str255            numString;
  1495.     register short    counter;
  1496.     
  1497.     // first of all, make sure we don't have the max number of windows already
  1498.     // open.  if so, return.
  1499.     // also, don't update the gNumWindows counter here, just in case elsewhere in
  1500.     // creating the window we have to bail out...wait until the end
  1501.     
  1502.     if ( gNumWindows >= kMaxNumberOfOpenWindows )
  1503.         return -1;  // "arbitrary" error code
  1504.     
  1505.     // make sure the numString is nil'ed out...
  1506.     
  1507.     for ( counter = 0; counter < 256; counter++ )
  1508.     {
  1509.         numString[ counter ] = nil;
  1510.         titleString[ counter ] = nil;
  1511.     }
  1512.     
  1513.     //    allocate a relocateable block to hold a document record
  1514.     
  1515.     hDocument = (DocumentHandle)NewHandleClear( sizeof( DocumentRecord ) );
  1516.     err = MemError();
  1517.     if ( err != noErr )
  1518.     {
  1519.         if ( err == memFullErr )
  1520.         {
  1521.             // if we ran out of memory, see if we might be able to free some up.
  1522.             // an already open window (with a WASTE instance) might have a sound
  1523.             // playing in it.  if so, we'll stop the current sound, free up the
  1524.             // memory the sound was occupying, and try again hoping this might help.
  1525.             
  1526.             if (SoundIsPlaying())
  1527.             {
  1528.                 // first, stop the sound, if any
  1529.             
  1530.                 StopCurrentSound();
  1531.                 
  1532.                 // now try to allocate the doc rec block
  1533.                 
  1534.                 hDocument = (DocumentHandle)NewHandleClear( sizeof( DocumentRecord ) );
  1535.                 
  1536.                 err = MemError();
  1537.             }
  1538.         }
  1539.         
  1540.         if ( err != noErr )
  1541.             HsoiDoError( rWindowErrorStrings, strCrWindNewPtrClear, err, kErrNote );
  1542.         return err;
  1543.     }
  1544.  
  1545.     //    create the window from a 'WIND' template: the window is initially invisible
  1546.     //    if ColorQuickDraw is available, create a color window
  1547.     
  1548.     if ( gHasColorQD )
  1549.         window = GetNewCWindow( rMainWindow, nil, MOVE_TO_FRONT );
  1550.     else
  1551.         window = GetNewWindow( rMainWindow, nil, MOVE_TO_FRONT );
  1552.     
  1553.     //    make sure we got a window
  1554.     
  1555.     if ( window == nil )
  1556.     {
  1557.         // just like with the above check for playing sounds, try that same thing here
  1558.         
  1559.         if ( SoundIsPlaying() )
  1560.         {
  1561.             StopCurrentSound();
  1562.  
  1563.             if ( gHasColorQD )
  1564.                 window = GetNewCWindow( rMainWindow, nil, MOVE_TO_FRONT );
  1565.             else
  1566.                 window = GetNewWindow( rMainWindow, nil, MOVE_TO_FRONT );
  1567.         }
  1568.         
  1569.         if ( window == nil )        
  1570.         {
  1571.             // failed (again), probably nothing we can do...
  1572.             
  1573.             HsoiForgetHandle( (Handle *)&hDocument );
  1574.             HsoiDoError( rWindowErrorStrings, strCrWindGetNewWind, memFullErr, kErrNote );
  1575.             return memFullErr;
  1576.         }
  1577.     }
  1578.  
  1579.     // link the document record to the window, and the other way around
  1580.     
  1581.     SetWRefCon( window, (long)hDocument );
  1582.     (*hDocument)->owner = window;
  1583.     
  1584.     // tho the toolbox should do this for us, let's do it ourselves to ensure the correct
  1585.     // value will be placed in the windowKind field
  1586.     
  1587.     SetWindowKind( window, kDocumentKind );
  1588.  
  1589.     // we got a window, so tell QuickDraw where to draw...
  1590.     
  1591.     SetPortWindowPort( window );
  1592.     
  1593.     // let's rename the window to keep a nice running count of the number of untitled
  1594.     // windows we have open(ed).  we only need to do this for untitled windows (i.e.
  1595.     // pFileSpec == nil).  we set the window title to the file name (if there is a file)
  1596.     // later on, and why waste time with doing this twice cause the first would be
  1597.     // moot and a waste
  1598.     
  1599.     if ( pFileSpec == nil )
  1600.     {
  1601.         gNewWindowCount++;
  1602.     
  1603.         GetIndString( titleString, rUntitledWindowStrings, strUntitledWindow );
  1604.         NumToString( gNewWindowCount, numString );
  1605.         HsoiConcatString( titleString, numString );
  1606.         SetWTitle( window, titleString );
  1607.     }
  1608.         
  1609.     /*    let's adjust the window and it's various parts.  We want the textRect (the initial
  1610.         viewRect and destRect of the WE instance) to be the same as the "viewRect" for
  1611.         printing...this way, what the user sees on the screen and what the user gets when
  1612.         they print will look and be the same.
  1613.         
  1614.         Depending on the size of the users monitor tho, this could extend off the end of
  1615.         the screen, so we'll have to adjust the size of the window to fit into the screen.
  1616.         At the same time, we don't want the window to cover the entire monitor...that's
  1617.         annoying for people with 21" monitors.
  1618.     */
  1619.  
  1620.     // first, figure out the ideal window size
  1621.     
  1622.     HsoiCalcIdealWindowSize( &windowRect );
  1623.     
  1624.     // assign this ideal size to the window
  1625.     
  1626.     SizeWindow( window, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, true );
  1627.     
  1628.     // figure out the text rect (and convert to a LongRect for passing to WENew)
  1629.     
  1630.     HsoiCalcTextRect( window, &textRect );
  1631.     WERectToLongRect( &textRect, &longTextRect );
  1632.  
  1633.     // it's possible that the window in it's ideal state will extend off the edges of
  1634.     // the monitor.  readjust the window's portRect to fit inside the screen boundries
  1635.     
  1636.     HsoiAdjustWindowIntoScreen( window );
  1637.  
  1638.     // assign the preference's default settings to the port so things will show up
  1639.     // properly (need to do this before WENew() is called)
  1640.     
  1641.     GetFNum( gMyPrefs.defFont, &GetWindowPort(window)->txFont );
  1642.     GetWindowPort(window)->txFace = gMyPrefs.defFace;
  1643.     GetWindowPort(window)->txSize = gMyPrefs.defSize;
  1644.     if ( gHasColorQD )
  1645.         GetWindowPort(window)->rgbFgColor = gMyPrefs.defColor;
  1646.     else
  1647.         GetWindowPort(window)->fgColor = blackColor;
  1648.  
  1649.     /*
  1650.         create the WE instance, enabling certain functions.  the functions we can/cannot
  1651.         do are listed in WASTE.h...and we're not supporting (right now) are:
  1652.         weDoReadOnly, weDoInhibitRecal.
  1653.  
  1654.         We'll use whatever our prefs setup calls for plus enabling undo support and the ability
  1655.         to use temporary memory.    
  1656.     */        
  1657.     
  1658.     err = WENew( &longTextRect, &longTextRect, gMyPrefs.defFeatures + 
  1659.                                                weDoUndo + 
  1660.                                                weDoUseTempMem, &we );
  1661.     
  1662.     if ( err != noErr )
  1663.     {
  1664.         HsoiDoError( rWindowErrorStrings, strCrWindWENew, err, kErrNote );
  1665.         return err;
  1666.     }
  1667.  
  1668.     //    save a reference to the window in the WE instance
  1669.     err = WESetInfo( weRefCon, &window, we );
  1670.     if ( err != noErr )
  1671.     {
  1672.         HsoiDoError( rWindowErrorStrings, strCrWindWESetInfo, err, kErrNote );
  1673.         return err;
  1674.     }
  1675.     
  1676.     //    now the other way around:  save the WE handle in the document record
  1677.     
  1678.     (*hDocument)->we = we;
  1679.  
  1680.     // set the alignment to weFlushLeft so "slop recalc" is disabled
  1681.     
  1682.     WESetAlignment( weFlushLeft, we );
  1683.         
  1684.     // and set it again to whatever the preferences default is, but only if the
  1685.     // prefs default isn't weFlushLeft (why do it 2 times and waste processor time?)
  1686.     
  1687.     if( gMyPrefs.defAlign != weFlushLeft )        
  1688.         WESetAlignment( gMyPrefs.defAlign, we );
  1689.         
  1690.     // and if they want Tab Hooks, let's install them
  1691.     
  1692. //    if ( pFileSpec == nil )    
  1693. //    {
  1694.         if ( gMyPrefs.useTabHooks )
  1695.         {
  1696.              if ( WEIsTabHooks( we ) == false )
  1697.             {
  1698.                 // left-align the text
  1699.                 WESetAlignment( weFlushLeft, we );
  1700.             
  1701.                 // install them
  1702.                 WEInstallTabHooks( we );
  1703.             }
  1704.             else
  1705.             {
  1706.                 // should never have to remove them, but we'll put this here for completeness
  1707.                     WERemoveTabHooks( we );
  1708.             }
  1709.         }
  1710. //    }
  1711.                 
  1712.     // create routine descriptors for the WASTE callbacks
  1713.     
  1714.     if ( sWEScroller == nil )
  1715.     {
  1716.         sWEScroller = NewWEScrollProc( hsoiTextScrolled );
  1717. //        sWEDragTranslator = NewWETranslateDragProc( HsoiTranslateDrag );
  1718.     }
  1719.     
  1720.     //    set up our callbacks
  1721.     
  1722.     err = WESetInfo( weScrollProc, &sWEScroller, we );
  1723.     if ( err != noErr )
  1724.     {
  1725.         HsoiDoError( rWindowErrorStrings, strCrWindWESetInfoScroll, err, kErrNote );
  1726.         return err;
  1727.     }
  1728. /*
  1729.     err = WESetInfo( weTranslateDragHook, &sWEDragTranslator, we );
  1730.     if ( err != noErr )
  1731.     {
  1732.         HsoiDoError( rWindowErrorStrings, strCrWindWESetInfoDrag, err, kErrNote );
  1733.         return err;
  1734.     }
  1735. */    
  1736.     //    create a scroll bar from a 'CNTL' template
  1737.     
  1738.     //    first vertical
  1739.     
  1740.     bar = GetNewControl( rScrollBarTemplate, window );
  1741.     if ( bar == nil )
  1742.     {
  1743.         err = -1;
  1744.         HsoiDoError( rWindowErrorStrings, strCrWindGetNewControlV, err, kErrNote );
  1745.         return err;
  1746.     }
  1747.     
  1748.     HiliteControl( bar, kControlDisabledPart );
  1749.     
  1750.     //    attach a LongControl record to the scroll bar:  this allows us to use long
  1751.     //    settings and thus scroll text taller than 32,767 pixels
  1752.     
  1753.     err = HsoiLCAttach( bar );
  1754.     if ( err != noErr )
  1755.     {
  1756.         HsoiDoError( rWindowErrorStrings, strCrWindLCAttachV, err, kErrNote );
  1757.         return err;
  1758.     }
  1759.     
  1760.     //    save control handle in the document record
  1761.     
  1762.     ((*hDocument)->scrollBars).v = bar;    
  1763.     
  1764.     //    now horizontal
  1765.     
  1766.     bar = GetNewControl( rScrollBarTemplate, window );
  1767.     if ( bar == nil )
  1768.     {
  1769.         err = -1;
  1770.         HsoiDoError( rWindowErrorStrings, strCrWindGetNewControlH, err, kErrNote );
  1771.         return err;
  1772.     }
  1773.     
  1774.     HiliteControl( bar, kControlDisabledPart );
  1775.     
  1776.     //    attach a LongControl record to the scroll bar:  this allows us to use long
  1777.     //    settings and thus scroll text taller than 32,767 pixels
  1778.     
  1779.     err = HsoiLCAttach( bar );
  1780.     if ( err != noErr )
  1781.     {
  1782.         HsoiDoError( rWindowErrorStrings, strCrWindLCAttachH, err, kErrNote );
  1783.         return err;
  1784.     }
  1785.     
  1786.     //    save control handle in the document record
  1787.     
  1788.     ((*hDocument)->scrollBars).h = bar;
  1789.         
  1790.     //    ViewChanged() adjusts the scroll bars rectangles to the window frame
  1791.     
  1792.     HsoiViewChanged( window );
  1793.     
  1794.     //    if pFileSpec is not nil, it points to a file to read, so let's read it!
  1795.     
  1796.     if ( pFileSpec != nil )
  1797.     {
  1798.         // turn the cursor into a wristwatch because this can be a lengthy operation
  1799.         // actually, we'll do this in HsoiReadTextFile cause that's really where the
  1800.         // time consumption is, and that way, any other function that calls HsoiReadTextFile
  1801.         // can get a VBL cursor, and we don't call one here to conflict with that
  1802.         
  1803.         //SetCursor( *gWaitCursor );
  1804.         //HsoiStartVBLSpinning();
  1805.         
  1806.         //    retrieve file infomation
  1807.         
  1808.         err = FSpGetFInfo( pFileSpec, &fileInfo );
  1809.         if ( err != noErr )
  1810.         {
  1811.             HsoiDoError( rWindowErrorStrings, strCrWindFSpGetFInfo, err, kErrNote );
  1812.             return err;
  1813.         }
  1814.         
  1815.         //    make sure we recognize the file type
  1816.     
  1817.         // if it happened to be the prefs file they double clicked on, let's just skip it
  1818.         // and make sure to "nil" some things out
  1819.         
  1820.         if ( fileInfo.fdType == TYPE_PREFERENCES )
  1821.         {
  1822.             (*hDocument)->fileAlias = nil;
  1823.             SetCursor( &qd.arrow );
  1824.             goto skipit;
  1825.         }
  1826.  
  1827.         // we only deal with files of 'TEXT' and 'ttro' (text and read-only-text files)
  1828.         
  1829.         if ( (fileInfo.fdType != TYPE_TEXT ) && ( fileInfo.fdType != TYPE_TEXT_READ_ONLY ) )
  1830.         {
  1831.             err = -1;
  1832.             HsoiDoError( rWindowErrorStrings, strCrWindkTypeText, err, kErrNote );
  1833.             return err;
  1834.         }
  1835.         
  1836.         //    read in the file
  1837.         err = HsoiReadTextFile( pFileSpec, we );
  1838.         if ( err != noErr )
  1839.         {
  1840.             HsoiDoError( rWindowErrorStrings, strCrWindReadTextFile, err, kErrNote );
  1841.             return err;
  1842.         }
  1843.         
  1844.         //  you _could_ call WECalText here and recalculate the line breaks,
  1845.         //    but it's really unnecessary, and just tends to slow things down
  1846.         
  1847.         //    set the window title to the file name
  1848.         SetWTitle( window, pFileSpec->name );
  1849.         
  1850.         //    create an alias to keep track of the file
  1851.         err = NewAlias( nil, pFileSpec, &alHandle );
  1852.         (*hDocument)->fileAlias = (Handle)alHandle;
  1853.             
  1854.         //    if the file is a read-only file (type 'ttro'), go ahead and enable
  1855.         //    those flags
  1856.         
  1857.         if (fileInfo.fdType == TYPE_TEXT_READ_ONLY )
  1858.             WEFeatureFlag( weFReadOnly, weBitSet, we );
  1859.         
  1860.         //    let's make sure the cursor is happy...
  1861.         //InitCursor();
  1862.         //HsoiStopVBLSpinning();
  1863.         
  1864.     }    // end pFileSpec != nil
  1865.     else
  1866.         (*hDocument)->fileAlias = nil;
  1867.  
  1868. skipit:
  1869.         
  1870.     //    adjust scroll bar settings based on the total text height
  1871.  
  1872.     HsoiAdjustBars( window );
  1873.  
  1874.     // add to our open window count
  1875.     
  1876.     gNumWindows++;
  1877.     
  1878.     //    finally!  show the document window
  1879.  
  1880.     ShowWindow( window );
  1881.     
  1882.     // before we go, add this window to our Windows menu
  1883.     
  1884.     HsoiAddWindowToMenu( window );
  1885.     
  1886.     return err;
  1887. }
  1888.  
  1889. // the "low level" routine for axing a document window
  1890.  
  1891. void    HsoiDestroyWindow( WindowRef window )
  1892. {
  1893.     DocumentHandle        hDocument;
  1894.  
  1895.     // any function calling this routine ought to have done this already, but
  1896.     // just in case, we'll do it again to be sure.
  1897.     
  1898.     if ( SoundIsPlaying() )
  1899.         StopCurrentSound();
  1900.     
  1901.     // get the window's document record
  1902.  
  1903.     hDocument = HsoiGetWindowDocument(window);
  1904.     
  1905.     // remove the window from the Windows menu
  1906.     // (we have to do this before we destroy much else since this function
  1907.     // relies upon the DocumentRecord attached to the window)
  1908.     
  1909.     HsoiRemoveWindowFromMenu( window );
  1910.     
  1911.     //    destroy the WE record
  1912.     
  1913.     WEDispose( (*hDocument)->we );
  1914.     
  1915.     //    destory the LongControl records attached to the scroll bars
  1916.     
  1917.     HsoiLCDetach( ((*hDocument)->scrollBars).v );
  1918.     HsoiLCDetach( ((*hDocument)->scrollBars).h );
  1919.     
  1920.     //    dispose of the file alias, if any
  1921.     
  1922.     HsoiForgetHandle( &((*hDocument)->fileAlias) );
  1923.     
  1924.     //    destroy the window record and all associated data structures
  1925.     
  1926.     DisposeWindow( window );
  1927.     
  1928.     // dispose of the document record
  1929.     
  1930.     DisposeHandle( (Handle)hDocument );
  1931.     
  1932.     // decrement our open window counter.  note that we don't do this in HsoiDoClose
  1933.     // cause of how HsoiCreateWindow works...also, see HsoiDoRevert for more reasons.
  1934.     
  1935.     gNumWindows--;
  1936.     
  1937.     // adjust the menus to suit
  1938.     
  1939.     HsoiAdjustMenus();
  1940.     
  1941.     return;
  1942. }
  1943.  
  1944.  
  1945.  
  1946. /*
  1947.     This allows you to display the contents of the clipboard in a window.  This is a special
  1948.     window however.  For the most part, it will look like any regular text editing window
  1949.     (i.e. a window made by HsoiCreateWindow(), however, there will be an area just under
  1950.     the title bar to say what the clipboard contents is (text, sound, picture, etc).  Also,
  1951.     you cannot edit this window (obviously) by typing on it, cutting and pasting, etc.
  1952.     nor do drag and drop editing on it.)
  1953.  
  1954.     so you know, the little area just under the title bar saying what the contents is
  1955.     (just like "Show Clipboard" in the Finder) isn't implimented yet...i'm still working
  1956.     on it.
  1957.  
  1958.     note the similarity of this code to the code used in HsoiCreateWindow(), and note
  1959.     the differences too.  It'd be nice to just call HsoiCreateWindow() to do all this stuff,
  1960.     but due to a few differences in things, we have to just do it all over again.
  1961. */
  1962.  
  1963. OSErr    HsoiShowClipboard( void )
  1964. {
  1965.     OSErr            err = noErr;
  1966.     DocumentHandle    hDocument;
  1967.     Str255            windowTitle = NIL_STRING;
  1968.     Rect            windowRect;
  1969.     Rect            textRect;
  1970.     LongRect        longTextRect;
  1971.     WEReference        we;
  1972.     ControlRef        bar;
  1973.     Boolean            canPaste;
  1974.     Str255            tempStr = NIL_STRING;
  1975.     
  1976.     // to make life easier (i.e. kinda be lazy), if there is a currently playing
  1977.     // sound, we'll just stop it now.  saves us the trouble from checking a million
  1978.     // times in the code below (when we try to allocate document record space,
  1979.     // allocate the window, allocate other things, bring up the "empty/unknown"
  1980.     // alert, etc....)
  1981.     
  1982.     if ( SoundIsPlaying() )
  1983.         StopCurrentSound();
  1984.     
  1985.     if ( gClipboardWindow == nil )
  1986.     {
  1987.         //    allocate a relocateable block to hold a document record
  1988.  
  1989.         hDocument = (DocumentHandle)NewHandleClear( sizeof( DocumentRecord ) );
  1990.         err = MemError();
  1991.         if ( err != noErr )
  1992.         {
  1993.             HsoiDoError( rWindowErrorStrings, strCrWindNewPtrClear, err, kErrNote );
  1994.             return err;
  1995.         }
  1996.  
  1997.         //    create the window from a 'WIND' template: the window is initially invisible
  1998.         //    if ColorQuickDraw is available, create a color window
  1999.     
  2000.         if ( gHasColorQD )
  2001.             gClipboardWindow = GetNewCWindow( rMainWindow, nil, MOVE_TO_FRONT );
  2002.         else
  2003.             gClipboardWindow = GetNewWindow( rMainWindow, nil, MOVE_TO_FRONT );
  2004.     
  2005.         //    make sure we got a window
  2006.     
  2007.         if ( gClipboardWindow == nil )
  2008.         {
  2009.             HsoiForgetHandle( (Handle *)&hDocument );
  2010.             HsoiDoError( rWindowErrorStrings, strCrWindGetNewWind, memFullErr, kErrNote );
  2011.             return memFullErr;
  2012.         }
  2013.         
  2014.         // link the document record to the window and the other way around
  2015.         
  2016.         SetWRefCon( gClipboardWindow, (long)hDocument );
  2017.         (*hDocument)->owner = gClipboardWindow;
  2018.  
  2019.         // this window is of type kClipboardWindow (the windowKind), so we must
  2020.         // set this field right for easy identification of the clipboard window (and so
  2021.         // the function HsoiIsClipboardWindow() will work right :)
  2022.         
  2023.         SetWindowKind( gClipboardWindow, kClipboardKind );
  2024.         
  2025.         // set the port to the clipboard window
  2026.         
  2027.         SetPortWindowPort( gClipboardWindow );
  2028.         
  2029.         // change the window title appropriately
  2030.         
  2031.         GetIndString( windowTitle, rClipboardWindowStrings, strClipboardWindowTitle );
  2032.         SetWTitle( gClipboardWindow, windowTitle );
  2033.  
  2034.         /*    let's adjust the window and it's various parts.  We want the textRect (the initial
  2035.             viewRect and destRect of the WE instance) to be the same as the "viewRect" for
  2036.             printing...this way, what the user sees on the screen and what the user gets when
  2037.             they print will look and be the same.
  2038.             
  2039.             Depending on the size of the users monitor tho, this could extend off the end of
  2040.             the screen, so we'll have to adjust the size of the window to fit into the screen.
  2041.             At the same time, we don't want the window to cover the entire monitor...that's
  2042.             annoying for people with 21" monitors.
  2043.         */
  2044.     
  2045.         // first, figure out the ideal window size
  2046.         
  2047.         HsoiCalcIdealWindowSize( &windowRect );
  2048.         
  2049.         // assign this ideal size to the window
  2050.         
  2051.         SizeWindow( gClipboardWindow, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, true );
  2052.         
  2053.         // it's possible that the window in it's ideal state will extend off the edges of
  2054.         // the monitor.  readjust the window's portRect to fit inside the screen boundries
  2055.         
  2056.         HsoiAdjustWindowIntoScreen( gClipboardWindow );
  2057.         
  2058.         // figure out the text rect (and convert to a LongRect for passing to WENew)
  2059.         
  2060.         HsoiCalcTextRect( gClipboardWindow, &textRect );
  2061.         WERectToLongRect( &textRect, &longTextRect );
  2062.         
  2063.  
  2064.         // here's where we differ a bit.  default preferences are NOT set here.  we don't
  2065.         // really want the user to have too much control over how this window is created
  2066.         // cause of it's nature.  however, if you want to have the default prefs settings
  2067.         // for this window, assign them here
  2068.                 
  2069.         GetFNum( "\pGeneva", &GetWindowPort(gClipboardWindow)->txFont );
  2070.         GetWindowPort(gClipboardWindow)->txFace = normal;
  2071.         GetWindowPort(gClipboardWindow)->txSize = 9;
  2072.                 
  2073.         // also note, in WENew, we don't just pass the prefs settings, but our own.
  2074.         // do NOT pass weDoDragAndDrop even if the computer can support drag and drop.
  2075.         // this is cause we want to prevent all dragging and dropping to this window
  2076.         // (tho this might change in the future...we might not allow drops to the window,
  2077.         // but we might allow people to select and drag from this window cause this might
  2078.         // make use of the clipboard a little easier (you can just paste what you want
  2079.         // into a document, like if you copied too much stuff to the clipboard).
  2080.         // Also note, we make this window read-only, and undo is not enabled (tho this
  2081.         // probably can/will change if drag and drop gets supported)
  2082.  
  2083.         err = WENew( &longTextRect, &longTextRect, weDoAutoScroll + 
  2084.                                                        weDoOutlineHilite + 
  2085.                                                        weDoIntCutAndPaste + 
  2086.                                                        weDoUseTempMem + 
  2087.                                                        weDoDrawOffscreen,
  2088.                                                        &we);
  2089.         
  2090.         if ( err != noErr )
  2091.         {
  2092.             HsoiDoError( rWindowErrorStrings, strCrWindWENew, err, kErrNote );
  2093.             return err;
  2094.         }
  2095.                         
  2096.         err = WESetInfo( weRefCon, &gClipboardWindow, we );
  2097.         if ( err != noErr )
  2098.         {
  2099.             HsoiDoError( rWindowErrorStrings, strCrWindWESetInfo, err, kErrNote );
  2100.             return err;
  2101.         }
  2102.                 
  2103.         (*hDocument)->we = we;
  2104.         
  2105.         WESetAlignment( weFlushLeft, we );
  2106.             
  2107.         if ( sWEScroller == nil )
  2108.         {
  2109.             sWEScroller = NewWEScrollProc( hsoiTextScrolled );
  2110. //            sWEDragTranslator = NewWETranslateDragProc( HsoiTranslateDrag );
  2111.         }
  2112.         
  2113.         err = WESetInfo( weScrollProc, &sWEScroller, we );
  2114.         if ( err != noErr )
  2115.         {
  2116.             HsoiDoError( rWindowErrorStrings, strCrWindWESetInfoScroll, err, kErrNote );
  2117.             return err;
  2118.         }
  2119.     
  2120.         // we shouldn't have this in here really, but we'll leave it in for now for
  2121.         // possible later support of dragging from this window
  2122.         
  2123. /*        err = WESetInfo( weTranslateDragHook, &sWEDragTranslator, we );
  2124.         if ( err != noErr )
  2125.         {
  2126.             HsoiDoError( rWindowErrorStrings, strCrWindWESetInfoDrag, err, kErrNote );
  2127.             return err;
  2128.         }
  2129. */        
  2130.         //    create a scroll bar from a 'CNTL' template
  2131.         
  2132.         //    first vertical
  2133.         
  2134.         bar = GetNewControl( rScrollBarTemplate, gClipboardWindow );
  2135.         if ( bar == nil )
  2136.         {
  2137.             err = -1;
  2138.             HsoiDoError( rWindowErrorStrings, strCrWindGetNewControlV, err, kErrNote );
  2139.             return err;
  2140.         }
  2141.         
  2142.         HiliteControl( bar, kControlDisabledPart );
  2143.         
  2144.         //    attach a LongControl record to the scroll bar:  this allows us to use long
  2145.         //    settings and thus scroll text taller than 32,767 pixels
  2146.         
  2147.         err = HsoiLCAttach( bar );
  2148.         if ( err != noErr )
  2149.         {
  2150.             HsoiDoError( rWindowErrorStrings, strCrWindLCAttachV, err, kErrNote );
  2151.             return err;
  2152.         }
  2153.         
  2154.         //    save control handle in the document record
  2155.         
  2156.         ((*hDocument)->scrollBars).v = bar;
  2157.                 
  2158.         //    now horizontal
  2159.         
  2160.         bar = GetNewControl( rScrollBarTemplate, gClipboardWindow );
  2161.         if ( bar == nil )
  2162.         {
  2163.             err = -1;
  2164.             HsoiDoError( rWindowErrorStrings, strCrWindGetNewControlH, err, kErrNote );
  2165.             return err;
  2166.         }
  2167.         
  2168.         HiliteControl( bar, kControlDisabledPart );
  2169.         
  2170.         //    attach a LongControl record to the scroll bar:  this allows us to use long
  2171.         //    settings and thus scroll text taller than 32,767 pixels
  2172.         
  2173.         err = HsoiLCAttach( bar );
  2174.         if ( err != noErr )
  2175.         {
  2176.             HsoiDoError( rWindowErrorStrings, strCrWindLCAttachH, err, kErrNote );
  2177.             return err;
  2178.         }
  2179.         
  2180.         //    save control handle in the document record
  2181.         
  2182.         ((*hDocument)->scrollBars).h = bar;
  2183.                 
  2184.         //    ViewChanged() adjusts the scroll bars rectangles to the window frame
  2185.         
  2186.         HsoiViewChanged( gClipboardWindow );
  2187.         
  2188.         // there's no file associated with this, so make sure to set the fileAlias to nil
  2189.         
  2190.         (*hDocument)->fileAlias = nil;
  2191.                 
  2192.         //    adjust scroll bar settings based on the total text height
  2193.  
  2194.         HsoiAdjustBars( gClipboardWindow );
  2195.     }
  2196.     else
  2197.     {
  2198.         // we have the window.  let's hide it while we update things
  2199.         HideWindow( gClipboardWindow );
  2200.         
  2201.         // and we need to get the WASTE instance for that window
  2202.         
  2203.         we = HsoiGetWindowWE( gClipboardWindow );
  2204.     }
  2205.     
  2206.     // ok, now we have a window (either cause it was already around, or cause we just
  2207.     // created one).  Now, it's time to put something into it.
  2208.     
  2209.     // first, we check to see if there's anything on the clipboard to paste.
  2210.     // (disable the read-only bit if it's set cause WECanPaste checks for this)
  2211.     
  2212.     WEFeatureFlag( weFReadOnly, weBitClear, we );
  2213.  
  2214.     canPaste = WECanPaste( we );
  2215.     
  2216.     // now, let's paste in whatever we got
  2217.     
  2218.     if ( canPaste )
  2219.     {    
  2220.         // since WEPaste (and it's call to WEInsert) and WEDElete could take a long time
  2221.         // (like if the scrap is HUGE) can take a long time to perform, let's spin
  2222.         // our cursor.  Again (as with just about all my uses of the VBL spinning cursor)
  2223.         // this is good to do cause otherwise it sorta looks like the computer froze up
  2224.         // and this could worry the user...if the cursor is spinning, at least the
  2225.         // user is getting some feedback that the computer is running...and heck, if
  2226.         // we know the cursor is supposed to spin and it freezes up, then we probably
  2227.         // do have some sort of bad error and the computer is probably fubar
  2228.         
  2229.         // start the VBL spinning cursor
  2230.         
  2231.         HsoiStartVBLSpinning();
  2232.         
  2233.         // let's take whatever's currently in the window and delete it
  2234.         
  2235.         WESetSelection( 0, MAXLONG, we );
  2236.         err = WEDelete( we );
  2237.     
  2238.         // now paste whatever's on the scrap into the window
  2239.  
  2240.         //err = WEPaste( we );
  2241.         err = WEObjectsPaste( we );
  2242.         
  2243.         // and stop the spinning cursor
  2244.         
  2245.         HsoiStopVBLSpinning();
  2246.     }
  2247.     else
  2248.     {
  2249.         // we shouldn't use the prefs error alert box for this, but it's the "cleanest"
  2250.         // error box for use to use for this.  this will change when i get the error handlers
  2251.         // all re-written
  2252.         
  2253.         GetIndString( tempStr, rClipboardWindowStrings, strClipboardEmpty );
  2254.         ParamText( tempStr, NIL_STRING, NIL_STRING, NIL_STRING );
  2255.         NoteAlert( rPrefsErrorAlert, HsoiGetMyStandardDialogFilter() );
  2256.         
  2257.         // and through testing some other stuff, i found this as a bug...if !canPaste,
  2258.         // and there was something already in the window (like from a previous "sucessful"
  2259.         // ShowClipboard), the "old" window contents showed up!  not good...we should
  2260.         // make sure the window contains nothing.  so, simple fix:
  2261.         
  2262.         // and going with the above ( if canPaste ) thing about a long time, since
  2263.         // WEDelete could take a bit to do, let's sping the cursor
  2264.         
  2265.         HsoiStartVBLSpinning();
  2266.         
  2267.         WESetSelection( 0, MAXLONG, we );
  2268.         err = WEDelete( we );
  2269.         
  2270.         HsoiStopVBLSpinning();
  2271.     }
  2272.     
  2273.     // set the read-only bit.  users should not be able to cut/copy/paste to/from this
  2274.     // window for obvious reasons
  2275.     
  2276.     WEFeatureFlag( weFReadOnly, weBitSet, we );
  2277.  
  2278.     // now we can show it and all that good stuff
  2279.     
  2280.     ShowWindow( gClipboardWindow );
  2281.     SelectWindow( gClipboardWindow );
  2282.     SetPortWindowPort( gClipboardWindow );
  2283.     
  2284.     // force a menu update
  2285.  
  2286.     HsoiAdjustMenus();
  2287.  
  2288.     
  2289.     return err;
  2290. }
  2291.  
  2292.  
  2293. // since this is called from within HsoiDoUpdate(), there is no need to call BeginUpdate()
  2294. // EndUpdate() and to set the port to the window, cause HsoiDoUpdate() handles all of
  2295. // that stuff.  however, if you call HsoiDoClipboardUpdate() from anywhere else, see if
  2296. // you can just call HsoiDoUpdate() instead, else make sure to set things properly
  2297. // and call proper functions so a proper update can be done.
  2298.  
  2299. void    HsoiDoClipboardUpdate( WindowRef window )
  2300. {
  2301.     if ( window != gClipboardWindow )
  2302.     {
  2303.         SysBeep(1);
  2304.         return;
  2305.     }
  2306.     
  2307.     if ( !HsoiIsClipboardWindow( window ) )
  2308.     {
  2309.         SysBeep(1);
  2310.         SysBeep(1);
  2311.         return;
  2312.     }
  2313.     
  2314.     /*    this isn't working right...i'm trying to have it draw a string that will tell
  2315.         the user what type of data the clipboard contains.  it's not working.
  2316.         (it would be just like the "Show Clipboard" in the Finder.  And if this
  2317.         got working, that alert in HsoiShowClipboard() about empty/unknown would
  2318.         then no longer be necessary.  if it was empty, say so, if it's known,
  2319.         say what it is, and default to unknown.
  2320.         
  2321.         oh well, i've pretty much removed all the relevant code in HsoiShowClipboard
  2322.         that would deal with this, and this function can just wait.
  2323.         
  2324.         First, my drawing routines might not be right.  second, I'm reusing a lot
  2325.         of CreateWindow util routines to do this, and that's probably the problem.  I
  2326.         probably need to create a whole new slew of things just to work with this
  2327.         (a new Resize, a new ViewChanged, a new scroll proc, etc etc)
  2328.         
  2329.         second, and i think due to the above, when you select stuff in the window
  2330.         (and probably also due to the following code, it draws funky lines all over
  2331.         the place...it doesn't look good.
  2332.     /*
  2333.     PenNormal();
  2334.     
  2335.     MoveTo( window->portRect.left + 1, window->portRect.top - 1 );
  2336.     ShowPen();
  2337.     LineTo( window->portRect.right - 1, window->portRect.top - 1 );
  2338.     */
  2339.     
  2340.  
  2341.  
  2342.     return;
  2343. }